package handler

import (
	"crypto"
	_ "crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strings"

	"gitcode.net/togolife/nfttoken/account"
	"gitcode.net/togolife/nfttoken/common"
	"gitcode.net/togolife/nfttoken/trade"
)

type QueryAccountInput struct {
	Sign string `json:"sign"`
}

type QueryAccountOutput struct {
	RetCode  int      `json:"retCode"`
	RetMsg   string   `json:"retMsg"`
	Num      int      `json:"num,omitempty"`
	Accounts []string `json:"accounts,omitempty"`
}

func QueryAccountRet(w http.ResponseWriter, out *QueryAccountOutput) {
	v, err := json.Marshal(out)
	if err != nil {
		return
	}
	w.Write(v)
}

func setQueryAccountOutput(out *QueryAccountOutput, err common.NftError) {
	out.RetCode = err.ErrorNo
	out.RetMsg = err.ErrorMsg
}

func (input *QueryAccountInput) checkSign() bool {
	s := "sign=" + hdConf.SignRandomKey
	m := crypto.SHA256.New()
	m.Write([]byte(s))
	v := hex.EncodeToString(m.Sum(nil))
	return v == input.Sign
}

func QueryAccountIf(w http.ResponseWriter, req *http.Request) {
	input := QueryAccountInput{}
	output := QueryAccountOutput{}
	defer QueryAccountRet(w, &output)
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		hdLog.LogE("Read QueryAccount http request body failed! [%v]", err)
		setQueryAccountOutput(&output, common.NewError(400))
		return
	}
	err = json.Unmarshal(body, &input)
	if err != nil {
		hdLog.LogE("Decode QueryAccount http request body failed! [%v]", err)
		setQueryAccountOutput(&output, common.NewError(400))
		return
	}
	if !input.checkSign() {
		hdLog.LogE("Check input sign failed!")
		setQueryAccountOutput(&output, common.NewError(401))
		return
	}
	output.Accounts = account.GetCurrentAccounts()
	output.Num = len(output.Accounts)
	setQueryAccountOutput(&output, common.NewError(200))
	return
}

type QueryTradeInput struct {
	ReqChain string `json:"reqChain"`
	TxHash   string `json:"txHash"`
	Sign     string `json:"sign"`
}

type QueryTradeOutput struct {
	RetCode int    `json:"retCode"`
	RetMsg  string `json:"retMsg"`
	Status  int    `json:"status,omitempty"`
}

func QueryTradeRet(w http.ResponseWriter, out *QueryTradeOutput) {
	v, err := json.Marshal(out)
	if err != nil {
		return
	}
	w.Write(v)
}

func setQueryTradeOutput(out *QueryTradeOutput, err common.NftError) {
	out.RetCode = err.ErrorNo
	out.RetMsg = err.ErrorMsg
}

func (input *QueryTradeInput) checkSign() bool {
	s := "reqChain=" + input.ReqChain
	s += "&sign=" + hdConf.SignRandomKey
	s += "&txHash=" + input.TxHash
	m := crypto.SHA256.New()
	m.Write([]byte(s))
	v := hex.EncodeToString(m.Sum(nil))
	return v == input.Sign
}

func QueryTradeIf(w http.ResponseWriter, req *http.Request) {
	input := QueryTradeInput{}
	output := QueryTradeOutput{}
	defer QueryTradeRet(w, &output)
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		hdLog.LogE("Read QueryTrade http request body failed! [%v]", err)
		setQueryTradeOutput(&output, common.NewError(400))
		return
	}
	err = json.Unmarshal(body, &input)
	if err != nil {
		hdLog.LogE("Decode QueryTrade http request body failed! [%v]", err)
		setQueryTradeOutput(&output, common.NewError(400))
		return
	}
	if !input.checkSign() {
		hdLog.LogE("Check input sign failed!")
		setQueryTradeOutput(&output, common.NewError(401))
		return
	}
	v, nftErr := trade.QueryTransactionResult(input.ReqChain, input.TxHash)
	if !common.Valid(nftErr) {
		hdLog.LogE("QueryTrade failed [%v]", nftErr.ErrorMsg)
		setQueryTradeOutput(&output, nftErr)
		return
	}
	output.Status = v
	setQueryTradeOutput(&output, common.NewError(200))
	return
}

type QueryTotalInput struct {
	ReqChain     string `json:"reqChain"`
	ContractAddr string `json:"contractAddr"`
	Sign         string `json:"sign"`
}

type QueryTotalOutput struct {
	RetCode  int    `json:"retCode"`
	RetMsg   string `json:"retMsg"`
	TotalNum string `json:"totalNum,omitempty"`
}

func QueryTotalRet(w http.ResponseWriter, out *QueryTotalOutput) {
	v, err := json.Marshal(out)
	if err != nil {
		return
	}
	w.Write(v)
}

func setQueryTotalOutput(out *QueryTotalOutput, err common.NftError) {
	out.RetCode = err.ErrorNo
	out.RetMsg = err.ErrorMsg
}

func (input *QueryTotalInput) checkSign() bool {
	s := "contractAddr=" + input.ContractAddr
	s += "&reqChain=" + input.ReqChain
	s += "&sign=" + hdConf.SignRandomKey
	m := crypto.SHA256.New()
	m.Write([]byte(s))
	v := hex.EncodeToString(m.Sum(nil))
	return v == input.Sign
}

func QueryTotalIf(w http.ResponseWriter, req *http.Request) {
	input := QueryTotalInput{}
	output := QueryTotalOutput{}
	defer QueryTotalRet(w, &output)
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		hdLog.LogE("Read QueryTotal http request body failed! [%v]", err)
		setQueryTotalOutput(&output, common.NewError(400))
		return
	}
	err = json.Unmarshal(body, &input)
	if err != nil {
		hdLog.LogE("Decode QueryTotal http request body failed! [%v] body is [%v]", err, string(body))
		setQueryTotalOutput(&output, common.NewError(400))
		return
	}
	if !input.checkSign() {
		hdLog.LogE("Check input sign failed!")
		setQueryTotalOutput(&output, common.NewError(401))
		return
	}
	v, nftErr := trade.TotalSupply(input.ReqChain, input.ContractAddr)
	if !common.Valid(nftErr) {
		hdLog.LogE("QueryTotal failed [%v]", nftErr.ErrorMsg)
		setQueryTotalOutput(&output, nftErr)
		return
	}
	output.TotalNum = v
	setQueryTotalOutput(&output, common.NewError(200))
	return
}

type QueryTokenInput struct {
	ReqChain     string `json:"reqChain"`
	ContractAddr string `json:"contractAddr"`
	TokenId      string `json:"tokenId"`
	Sign         string `json:"sign"`
}

type QueryTokenOutput struct {
	RetCode int    `json:"retCode"`
	RetMsg  string `json:"retMsg"`
	Uri     string `json:"uri,omitempty"`
	Owner   string `json:"owner,omitempty"`
}

func QueryTokenRet(w http.ResponseWriter, out *QueryTokenOutput) {
	v, err := json.Marshal(out)
	if err != nil {
		return
	}
	w.Write(v)
}

func setQueryTokenOutput(out *QueryTokenOutput, err common.NftError) {
	out.RetCode = err.ErrorNo
	out.RetMsg = err.ErrorMsg
}

func (input *QueryTokenInput) checkSign() bool {
	s := "contractAddr=" + input.ContractAddr
	s += "&reqChain=" + input.ReqChain
	s += "&sign=" + hdConf.SignRandomKey
	s += "&tokenId=" + input.TokenId
	m := crypto.SHA256.New()
	m.Write([]byte(s))
	v := hex.EncodeToString(m.Sum(nil))
	return v == input.Sign
}

func QueryTokenIf(w http.ResponseWriter, req *http.Request) {
	input := QueryTokenInput{}
	output := QueryTokenOutput{}
	defer QueryTokenRet(w, &output)
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		hdLog.LogE("Read QueryToken http request body failed! [%v]", err)
		setQueryTokenOutput(&output, common.NewError(400))
		return
	}
	err = json.Unmarshal(body, &input)
	if err != nil {
		hdLog.LogE("Decode QueryToken http request body failed! [%v]", err)
		setQueryTokenOutput(&output, common.NewError(400))
		return
	}
	if !input.checkSign() {
		hdLog.LogE("Check input sign failed!")
		setQueryTokenOutput(&output, common.NewError(401))
		return
	}
	v, nftErr := trade.OwnerOf(input.ReqChain, input.ContractAddr, input.TokenId)
	if !common.Valid(nftErr) {
		hdLog.LogE("QueryToken owner failed [%v]", nftErr.ErrorMsg)
		setQueryTokenOutput(&output, nftErr)
		return
	}
	output.Owner = strings.ToLower(v)
	v, nftErr = trade.TokenUri(input.ReqChain, input.ContractAddr, input.TokenId)
	if !common.Valid(nftErr) {
		hdLog.LogE("QueryToken uri failed [%v]", nftErr.ErrorMsg)
		setQueryTokenOutput(&output, nftErr)
		return
	}
	output.Uri = v
	setQueryTokenOutput(&output, common.NewError(200))
	return
}

type QueryBalanceInput struct {
	ReqChain     string `json:"reqChain"`
	ContractAddr string `json:"contractAddr"`
	Address      string `json:"address"`
	Sign         string `json:"sign"`
}

type QueryBalanceOutput struct {
	RetCode int    `json:"retCode"`
	RetMsg  string `json:"retMsg"`
	Num     string `json:"num,omitempty"`
}

func QueryBalanceRet(w http.ResponseWriter, out *QueryBalanceOutput) {
	v, err := json.Marshal(out)
	if err != nil {
		return
	}
	w.Write(v)
}

func setQueryBalanceOutput(out *QueryBalanceOutput, err common.NftError) {
	out.RetCode = err.ErrorNo
	out.RetMsg = err.ErrorMsg
}

func (input *QueryBalanceInput) checkSign() bool {
	s := "address=" + input.Address
	s += "&contractAddr=" + input.ContractAddr
	s += "&reqChain=" + input.ReqChain
	s += "&sign=" + hdConf.SignRandomKey
	m := crypto.SHA256.New()
	m.Write([]byte(s))
	v := hex.EncodeToString(m.Sum(nil))
	return v == input.Sign
}

func QueryBalanceIf(w http.ResponseWriter, req *http.Request) {
	input := QueryBalanceInput{}
	output := QueryBalanceOutput{}
	defer QueryBalanceRet(w, &output)
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		hdLog.LogE("Read QueryBalance http request body failed! [%v]", err)
		setQueryBalanceOutput(&output, common.NewError(400))
		return
	}
	err = json.Unmarshal(body, &input)
	if err != nil {
		hdLog.LogE("Decode QueryBalance http request body failed! [%v]", err)
		setQueryBalanceOutput(&output, common.NewError(400))
		return
	}
	if !input.checkSign() {
		hdLog.LogE("Check input sign failed!")
		setQueryBalanceOutput(&output, common.NewError(401))
		return
	}
	v, nftErr := trade.BalanceOf(input.ReqChain, input.ContractAddr, input.Address)
	if !common.Valid(nftErr) {
		hdLog.LogE("QueryBalance failed [%v]", nftErr.ErrorMsg)
		setQueryBalanceOutput(&output, nftErr)
		return
	}
	output.Num = v
	setQueryBalanceOutput(&output, common.NewError(200))
	return
}

type QueryApprovedInput struct {
	ReqChain     string `json:"reqChain"`
	ContractAddr string `json:"contractAddr"`
	TokenId      string `json:"tokenId"`
	Sign         string `json:"sign"`
}

type QueryApprovedOutput struct {
	RetCode  int    `json:"retCode"`
	RetMsg   string `json:"retMsg"`
	Approved string `json:"approvedAddr,omitempty"`
}

func QueryApprovedRet(w http.ResponseWriter, out *QueryApprovedOutput) {
	v, err := json.Marshal(out)
	if err != nil {
		return
	}
	w.Write(v)
}

func setQueryApprovedOutput(out *QueryApprovedOutput, err common.NftError) {
	out.RetCode = err.ErrorNo
	out.RetMsg = err.ErrorMsg
}

func (input *QueryApprovedInput) checkSign() bool {
	s := "contractAddr=" + input.ContractAddr
	s += "&reqChain=" + input.ReqChain
	s += "&sign=" + hdConf.SignRandomKey
	s += "&tokenId=" + input.TokenId
	m := crypto.SHA256.New()
	m.Write([]byte(s))
	v := hex.EncodeToString(m.Sum(nil))
	return v == input.Sign
}

func QueryApprovedIf(w http.ResponseWriter, req *http.Request) {
	input := QueryApprovedInput{}
	output := QueryApprovedOutput{}
	defer QueryApprovedRet(w, &output)
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		hdLog.LogE("Read QueryApproved http request body failed! [%v]", err)
		setQueryApprovedOutput(&output, common.NewError(400))
		return
	}
	err = json.Unmarshal(body, &input)
	if err != nil {
		hdLog.LogE("Decode QueryApproved http request body failed! [%v]", err)
		setQueryApprovedOutput(&output, common.NewError(400))
		return
	}
	if !input.checkSign() {
		hdLog.LogE("Check input sign failed!")
		setQueryApprovedOutput(&output, common.NewError(401))
		return
	}
	v, nftErr := trade.GetApproved(input.ReqChain, input.ContractAddr, input.TokenId)
	if !common.Valid(nftErr) {
		hdLog.LogE("QueryApproved failed [%v]", nftErr.ErrorMsg)
		setQueryApprovedOutput(&output, nftErr)
		return
	}
	output.Approved = v
	setQueryApprovedOutput(&output, common.NewError(200))
	return
}

type QueryApprovedForAllInput struct {
	ReqChain     string `json:"reqChain"`
	ContractAddr string `json:"contractAddr"`
	OwnerAddr    string `json:"ownerAddr"`
	ApprovedAddr string `json:"approvedAddr"`
	Sign         string `json:"sign"`
}

type QueryApprovedForAllOutput struct {
	RetCode int    `json:"retCode"`
	RetMsg  string `json:"retMsg"`
	Flag    bool   `json:"flag"`
}

func QueryApprovedForAllRet(w http.ResponseWriter, out *QueryApprovedForAllOutput) {
	v, err := json.Marshal(out)
	if err != nil {
		return
	}
	w.Write(v)
}

func setQueryApprovedForAllOutput(out *QueryApprovedForAllOutput, err common.NftError) {
	out.RetCode = err.ErrorNo
	out.RetMsg = err.ErrorMsg
}

func (input *QueryApprovedForAllInput) checkSign() bool {
	s := "approvedAddr=" + input.ApprovedAddr
	s += "&contractAddr=" + input.ContractAddr
	s += "&reqChain=" + input.ReqChain
	s += "&sign=" + hdConf.SignRandomKey
	s += "&ownerAddr=" + input.OwnerAddr
	m := crypto.SHA256.New()
	m.Write([]byte(s))
	v := hex.EncodeToString(m.Sum(nil))
	return v == input.Sign
}

func QueryApprovedForAllIf(w http.ResponseWriter, req *http.Request) {
	input := QueryApprovedForAllInput{}
	output := QueryApprovedForAllOutput{}
	defer QueryApprovedForAllRet(w, &output)
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		hdLog.LogE("Read QueryApprovedForAll http request body failed! [%v]", err)
		setQueryApprovedForAllOutput(&output, common.NewError(400))
		return
	}
	err = json.Unmarshal(body, &input)
	if err != nil {
		hdLog.LogE("Decode QueryApprovedForAll http request body failed! [%v]", err)
		setQueryApprovedForAllOutput(&output, common.NewError(400))
		return
	}
	if !input.checkSign() {
		hdLog.LogE("Check input sign failed!")
		setQueryApprovedForAllOutput(&output, common.NewError(401))
		return
	}
	v, nftErr := trade.IsApprovedForAll(input.ReqChain, input.ContractAddr, input.OwnerAddr, input.ApprovedAddr)
	if !common.Valid(nftErr) {
		hdLog.LogE("QueryApprovedForAll failed [%v]", nftErr.ErrorMsg)
		setQueryApprovedForAllOutput(&output, nftErr)
		return
	}
	output.Flag = v
	setQueryApprovedForAllOutput(&output, common.NewError(200))
	return
}
